package com.syezon.wifikey.speed;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.text.SimpleDateFormat;

public class SpeedTestView3 extends View {

    private static final int COLOR_POINT = Color.parseColor("#1affffff");
    private static final int COLOR_PROGRESS = Color.parseColor("#ffffff");
    private int mRadius; // 画布边缘半径（去除padding后的半径）
    private int mStartAngle = 150; // 起始角度
    private int mSweepAngle = 240; // 绘制角度
    private int mMin = 0; // 最小值
    //    private int mMax = 1024 * 1024 * 8 * 1024 * 100; // 最大值
    private double mSpeed = 0; // speed
    private int mProgressWidth; // 进度圆弧宽度
    private int mMrkOffsetDegree = 1;
    private int mMarkWidth; // 刻度宽度
    private float mLength1; // 刻度顶部相对边缘的长度
    private float mLength3;//Text相对顶部的长度
    private int indicatorHeight = dp2px(93);
    private int indicatorWidth = dp2px(93);
    private int mPadding;
    private float mCenterX, mCenterY; // 圆心坐标
    private Paint mPaint;
    private Paint mTextPaint;
    private int mTextColor = Color.parseColor("#1affffff");
    private int mTextColorSwipe = Color.parseColor("#ffffffff");
    private RectF mRectInsideArc;
    private RectF mRectTextArc;
    private RectF mRectProgressArc;
    private Path mPath;

    private int innerRadius;

    private ValueAnimator mSpeedAnim;

    private ValueAnimator mPrepareAnim;
    private ValueAnimator mSweepPrepareAnim;
    private boolean mShowPrepareAnim = false;
    private boolean mSweepPrepareState;
    private boolean mLigthTextIndicator;
    private int mCurrentSweep;
    private PrepareCallBack mPrepareCallBack;

    private String[] mTextIndicator = new String[]{"0", "5", "10", "20", "50", "100", "150", "300", "500"};
    private SimpleDateFormat mDateFormat;

    public SpeedTestView3(Context context) {
        this(context, null);
    }

    public SpeedTestView3(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpeedTestView3(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mProgressWidth = dp2px(15);
        mMarkWidth = dp2px(1);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeCap(Paint.Cap.BUTT);
        mPaint.setColor(Color.WHITE);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(dp2px(10));

        mRectInsideArc = new RectF();
        mRectTextArc = new RectF();
        mRectProgressArc = new RectF();
        mPath = new Path();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mPadding = Math.max(
                Math.max(getPaddingLeft(), getPaddingTop()),
                Math.max(getPaddingRight(), getPaddingBottom())
        );
        setPadding(mPadding, mPadding, mPadding, mPadding);
        mLength1 = mPadding;
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float textTopHeight = Math.abs(fontMetrics.top);
        mLength3 = mLength1 + mProgressWidth + textTopHeight + dp2px(5);
        int width = resolveSize(dp2px(220), widthMeasureSpec);
        mRadius = (width - mPadding * 2) / 2;
        setMeasuredDimension(width, width - dp2px(50));
        mCenterX = mCenterY = getMeasuredWidth() / 2f;
        mRectInsideArc.set(
                mLength1 + mProgressWidth / 2,
                mLength1 + mProgressWidth / 2,
                getMeasuredWidth() - mLength1 - mProgressWidth / 2,
                getMeasuredWidth() - mLength1 - mProgressWidth / 2
        );
        mRectTextArc.set(
                mLength1 + mProgressWidth / 2 + dp2px(20),
                mLength1 + mProgressWidth / 2 + dp2px(20),
                getMeasuredWidth() - mLength1 - mProgressWidth / 2 - dp2px(20),
                getMeasuredWidth() - mLength1 - mProgressWidth / 2 - dp2px(20)
        );
        innerRadius = (int) (mRectTextArc.width() / 2);
        mRectProgressArc.set(
                mLength1 + mProgressWidth,
                mLength1 + mProgressWidth,
                getMeasuredWidth() - mLength1 - mProgressWidth,
                getMeasuredWidth() - mLength1 - mProgressWidth
        );
        mPaint.setTextSize(sp2px(10));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mShowPrepareAnim) {
            drawPrepare(canvas);
        } else {
            drawSpeed(canvas);
        }

    }

    private void drawSpeed(Canvas canvas) {
        mPaint.setColor(COLOR_POINT);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mProgressWidth);
        canvas.drawArc(mRectInsideArc, mStartAngle, mSweepAngle, false, mPaint);

        float sweepDegree = calculateRelativeAngleWithValue(mSpeed);
        mPaint.setColor(COLOR_PROGRESS);
        mPaint.setShader(generateProgressSweepGradient());
        canvas.drawArc(mRectInsideArc, mStartAngle, sweepDegree + 0.005f, false, mPaint);
        mPaint.setStyle(Paint.Style.FILL);

        mPaint.setAlpha(255);
        mPaint.setColor(COLOR_POINT);
        int startMarkDegree = mSweepAngle - mMrkOffsetDegree * 2;
        float a = sweepDegree;
        float b = mSweepAngle / 2f;
        mPaint.setStrokeWidth(mMarkWidth);
        mPaint.setShader(null);
        canvas.save();
        mPaint.setStyle(Paint.Style.FILL);
        int degree = startMarkDegree / 8;
        mTextPaint.setColor(a > b ? COLOR_PROGRESS : Color.WHITE);
        PointF centerTextPosition = getTextPosition(convertDegree(270), mTextIndicator[4]);
        canvas.drawText(mTextIndicator[4], centerTextPosition.x, centerTextPosition.y, mTextPaint);
        // 逆时针旋转
        for (int i = 4; i > 0; i--) {
            b -= degree;
            mPaint.setTextAlign(Paint.Align.CENTER);
            mTextPaint.setColor(a >= b ? COLOR_PROGRESS : Color.WHITE);
            String text = mTextIndicator[4 - i];
            PointF textAnchor = getTextPosition(convertDegree(270 - degree * (i)), text);
            canvas.drawText(text, textAnchor.x, textAnchor.y, mTextPaint);
        }
        canvas.restore();
        canvas.save();
        // 顺时针旋转
        b = mSweepAngle / 2f;
        for (int i = 0; i < 4; i++) {
            b += degree;
            mTextPaint.setColor(a >= b ? COLOR_PROGRESS : Color.WHITE);
            String text = mTextIndicator[5 + i];
            PointF textAnchor = getTextPosition(convertDegree(270 + degree * (i + 1)), text);
            canvas.drawText(text, textAnchor.x, textAnchor.y, mTextPaint);
        }
        canvas.restore();

        canvas.save();
        b = mSweepAngle / 2f;
        canvas.rotate(a - b, mCenterX, mCenterY);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.WHITE);
        mPaint.setAlpha(255);
        mPaint.setShader(generateInsideSweepGradient1());
        mPath.reset();
        mPath.moveTo(mCenterX - dp2px(3), mCenterY - dp2px(54));
        mPath.arcTo(new RectF(mCenterX - dp2px(3), mCenterY - dp2px(54 + 3), mCenterX + dp2px(3), mCenterY - dp2px(54 - 3)), 180, 180);
        mPath.lineTo(mCenterX + dp2px(5), mCenterY);
        mPath.arcTo(new RectF(mCenterX - dp2px(5), mCenterY - dp2px(5), mCenterX + dp2px(5), mCenterY + dp2px(5)), 0, 180, false);
        mPath.close();
        canvas.drawPath(mPath, mPaint);
        mPaint.setShader(null);
        canvas.restore();
    }

    private void drawPrepare(Canvas canvas) {
        mPaint.setColor(COLOR_POINT);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mProgressWidth);
        mPaint.setShader(null);
        canvas.drawArc(mRectInsideArc, mStartAngle, mCurrentSweep, false, mPaint);
        if (mSweepPrepareState) {
            float sweepDegree = calculateRelativeAngleWithValue(mSpeed);

            mPaint.setAlpha(255);
            mPaint.setColor(COLOR_POINT);
            int startMarkDegree = mSweepAngle - mMrkOffsetDegree * 2;
            float a = sweepDegree;
            float b = mSweepAngle / 2f;
            mPaint.setStrokeWidth(mMarkWidth);
            mPaint.setShader(null);
            canvas.save();
            mPaint.setStyle(Paint.Style.FILL);
            int degree = startMarkDegree / 8;
            mTextPaint.setColor(mLigthTextIndicator ? Color.WHITE : a > b ? Color.WHITE : COLOR_POINT);
            PointF centerTextPosition = getTextPosition(convertDegree(270), mTextIndicator[4]);
            canvas.drawText(mTextIndicator[4], centerTextPosition.x, centerTextPosition.y, mTextPaint);
            // 逆时针旋转
            for (int i = 4; i > 0; i--) {
                b -= degree;
                mPaint.setTextAlign(Paint.Align.CENTER);
                mTextPaint.setColor(mLigthTextIndicator ? Color.WHITE : a >= b ? Color.WHITE : COLOR_POINT);
                String text = mTextIndicator[4 - i];
                PointF textAnchor = getTextPosition(convertDegree(270 - degree * (i)), text);
                canvas.drawText(text, textAnchor.x, textAnchor.y, mTextPaint);
            }
            canvas.restore();
            canvas.save();
            // 顺时针旋转
            b = mSweepAngle / 2f;
            for (int i = 0; i < 4; i++) {
                b += degree;
                mTextPaint.setColor(mLigthTextIndicator ? Color.WHITE : a >= b ? Color.WHITE : COLOR_POINT);
                String text = mTextIndicator[5 + i];
                PointF textAnchor = getTextPosition(convertDegree(270 + degree * (i + 1)), text);
                canvas.drawText(text, textAnchor.x, textAnchor.y, mTextPaint);
            }
            canvas.restore();

            canvas.save();
            b = mSweepAngle / 2f;
            canvas.rotate(a - b, mCenterX, mCenterY);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(Color.WHITE);
            mPaint.setAlpha(255);
            mPaint.setShader(generateInsideSweepGradient1());
            mPath.reset();
            mPath.moveTo(mCenterX - dp2px(3), mCenterY - dp2px(54));
            mPath.arcTo(new RectF(mCenterX - dp2px(3), mCenterY - dp2px(54 + 3), mCenterX + dp2px(3), mCenterY - dp2px(54 - 3)), 180, 180);
            mPath.lineTo(mCenterX + dp2px(6), mCenterY);
            mPath.arcTo(new RectF(mCenterX - dp2px(6), mCenterY - dp2px(6), mCenterX + dp2px(6), mCenterY + dp2px(6)), 0, 180, false);
            mPath.close();
            canvas.drawPath(mPath, mPaint);
            mPaint.setShader(null);
            canvas.restore();
        }
    }

    private double convertDegree(double sweepDegree) {
        return 360 - sweepDegree;
    }

    private PointF getTextAnchor(double targetDegree, String text) {
        float y = 0;
        float x = 0;
        Rect outRect = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), outRect);
        float textWidth = mTextPaint.measureText(text);
        if (targetDegree <= 90 && targetDegree >= 0) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 + (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
//            x -= textWidth;
        } else if (targetDegree > 90 && targetDegree <= 180) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 - (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        } else if (targetDegree > 180 && targetDegree <= 270) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 - (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        } else {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 + (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
//            x -= textWidth;
        }
        return new PointF(x, y);
    }

    private PointF getTextPosition(double targetDegree, String text) {
        float y = 0;
        float x = 0;
        Rect outRect = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), outRect);
        if (targetDegree <= 90 && targetDegree >= 0) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 + (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        } else if (targetDegree > 90 && targetDegree <= 180) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 - (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        } else if (targetDegree > 180 && targetDegree <= 270) {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 - (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        } else {
            y = mRectTextArc.top + mRectTextArc.width() / 2 - (float) ((Math.sin(Math.toRadians(targetDegree)) * innerRadius));
            x = mRectTextArc.left + mRectTextArc.width() / 2 + (float) (Math.abs(Math.cos(Math.toRadians(targetDegree)) * innerRadius));
        }
        y += outRect.height();
        return new PointF(x, y);
    }

    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                Resources.getSystem().getDisplayMetrics());
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                Resources.getSystem().getDisplayMetrics());
    }

    private SweepGradient generateSweepGradient() {
        SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
                new int[]{Color.argb(0, 153, 237, 255), Color.argb(200, 153, 237, 255)},
                new float[]{0, calculateRelativeAngleWithValue(mSpeed) / 360}
        );
        Matrix matrix = new Matrix();
        matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
        sweepGradient.setLocalMatrix(matrix);

        return sweepGradient;
    }

    private SweepGradient generateInsideSweepGradient() {
        SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
                new int[]{Color.argb(255, 253, 226, 104), Color.argb(255, 153, 237, 169)},
                new float[]{0, 360}
        );
        Matrix matrix = new Matrix();
        matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
        sweepGradient.setLocalMatrix(matrix);
        return sweepGradient;
    }

    private RadialGradient generateProgressRadiaGradient() {
        RadialGradient sweepGradient = new RadialGradient(mCenterX, mCenterY, mCenterX / 2,
                new int[]{Color.parseColor("#0062DF95"), Color.parseColor("#0062DF95"), Color.parseColor("#0162DF95"), Color.parseColor("#1062DF95")}, null, Shader.TileMode.CLAMP
        );
        return sweepGradient;
    }

    private SweepGradient generateProgressSweepGradient() {
        SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
                new int[]{Color.parseColor("#FFFFFF"), Color.parseColor("#FFFFFF")},
                null
        );
        Matrix matrix = new Matrix();
        matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
        sweepGradient.setLocalMatrix(matrix);
        return sweepGradient;
    }

    private LinearGradient generateInsideSweepGradient1() {
        LinearGradient linearGradient = new LinearGradient(mCenterX, mCenterY, mCenterX, mCenterY - dp2px(93),
                new int[]{Color.parseColor("#FFFFFF"), Color.parseColor("#8FFFFFFF")},
                null, Shader.TileMode.CLAMP
        );
        return linearGradient;
    }

    /**
     * 相对起始角度计算信用分所对应的角度大小
     */
    private float calculateRelativeAngleWithValue(double value) {
        if (value <= 0) {
            return 0;
        }
        double degree = 0;
        int startMarkDegree = mSweepAngle - mMrkOffsetDegree * 2;
        int degreeDis = startMarkDegree / 8;
        if (value <= 5) {
            degree = (mMrkOffsetDegree + degreeDis * (value / 5.0));
        } else if (value <= 10) {
            degree = (mMrkOffsetDegree + degreeDis + degreeDis * ((value - 5) / 5.0));
        } else if (value <= 20) {
            degree = (mMrkOffsetDegree + degreeDis * 2 + degreeDis * ((value - 10) / 10.0));
        } else if (value <= (50)) {
            degree = (mMrkOffsetDegree + degreeDis * 3 + degreeDis * ((value - 20) / 30.0));
        } else if (value <= (100)) {
            degree = (mMrkOffsetDegree + degreeDis * 4 + degreeDis * ((value - 50) / 50.0));
        } else if (value <= (150)) {
            degree = (mMrkOffsetDegree + degreeDis * 5 + degreeDis * ((value - 100) / 50.0));
        } else if (value <= (300)) {
            degree = (mMrkOffsetDegree + degreeDis * 6 + degreeDis * ((value - 150) / 150.0));
        } else if (value <= (500)) {
            degree = (mMrkOffsetDegree + degreeDis * 7 + degreeDis * ((value - 300) / 200.0));
        } else {
            degree = startMarkDegree;
        }
        return (float) degree;
    }

    public double getCreditValue() {
        return mSpeed;
    }

    public void prepare(final PrepareCallBack prepareCallBack) {
        mSweepPrepareState = false;
        mShowPrepareAnim = true;
        mLigthTextIndicator = false;
        mSpeed = 0;
        if (mSpeedAnim != null && mSpeedAnim.isRunning()) {
            mSpeedAnim.cancel();
        }
        if (mPrepareAnim != null && mPrepareAnim.isRunning()) {
            mPrepareAnim.cancel();
        }
        if (mSweepPrepareAnim != null && mSweepPrepareAnim.isRunning()) {
            mSweepPrepareAnim.cancel();
        }
        mSweepPrepareAnim = ValueAnimator.ofFloat(0, 500, 0).setDuration(1000);
        mSweepPrepareAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mSpeed = (float) animation.getAnimatedValue();
                float animatedFraction = animation.getAnimatedFraction();
                if (animatedFraction > 0.5) {
                    mLigthTextIndicator = true;
                }
                invalidate();
            }
        });
        mSweepPrepareAnim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mSpeed = 0;
                mShowPrepareAnim = false;
                if (prepareCallBack != null) {
                    prepareCallBack.onPrepared();
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        mPrepareAnim = ValueAnimator.ofInt(0, mSweepAngle)
                .setDuration(1000);
        mPrepareAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentSweep = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        mPrepareAnim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mSweepPrepareState = true;
                mSweepPrepareAnim.start();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        mPrepareAnim.start();
    }

    /**
     * 设置信用值
     *
     * @param creditValue 信用值
     */
    public void setCreditValue(double creditValue) {
        mShowPrepareAnim = false;
        if (mSpeed == creditValue || creditValue < mMin) {
            return;
        }
        float startSpeed = (float) mSpeed;
        int targetSpeed = (int) creditValue;
        if (mSpeedAnim != null && mSpeedAnim.isRunning()) {
            mSpeedAnim.cancel();
        }
        mSpeedAnim = ObjectAnimator.ofFloat(startSpeed, targetSpeed)
                .setDuration(300);
        mSpeedAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mSpeed = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mSpeedAnim.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mSpeedAnim != null && mSpeedAnim.isRunning()) {
            mSpeedAnim.cancel();
        }
        if (mPrepareAnim != null && mPrepareAnim.isRunning()) {
            mPrepareAnim.cancel();
        }
        if (mSweepPrepareAnim != null && mSweepPrepareAnim.isRunning()) {
            mSweepPrepareAnim.cancel();
        }
    }

    public interface PrepareCallBack {
        void onPrepared();
    }
}
